﻿using System;
using System.Linq;
using DirectCanvas;
using DirectCanvas.Effects;
using DirectCanvas.Misc;
using System.Collections.Generic;
using DirectCanvas.Scenes;

namespace WindowsFormsTest.Demos.EffectsDemo
{
    class PixelShaderScene : Scene
    {
        /// <summary>
        /// Reference to our layer that we want to have our
        /// final rendered image on.  This is most likey a "Presenter"
        /// super class, at least in this sample
        /// </summary>
        private readonly DrawingLayer m_mainLayer;

        /// <summary>
        /// The width, in pixels, of the size of the scene were are making
        /// </summary>
        private const int SCENE_WIDTH = 1280;

        /// <summary>
        /// The height, in pixels, of the size of the scene we are making
        /// </summary>
        private const int SCENE_HEIGHT = 720;

        /// <summary>
        /// A drawing layer containing an image that has been loaded from a file
        /// </summary>
        private DrawingLayer m_bitmap;

        /// <summary>
        /// This temporary drawing layer is used to make our initial drawing,
        /// before we apply any shader effects
        /// </summary>
        private DrawingLayer m_tempLayer;

        /// <summary>
        /// The effect instance that does the pixel shader magic
        /// </summary>
        private RippleEffect m_rippleEffect;

        /// <summary>
        /// Current positions of down input events
        /// </summary>
        private Dictionary<int, RippleSettings> m_ripples;

        private RippleSettings nullRipple;

        public PixelShaderScene(DrawingLayer presenter)
        {
            m_mainLayer = presenter;
            m_ripples = new Dictionary<int, RippleSettings>();
            InitializeResources();
        }

        private void InitializeResources()
        {
            var factory = m_mainLayer.Factory;

            /* Initialize the drawing layer using our hard coded dimensions */
            m_tempLayer = factory.CreateDrawingLayer(SCENE_WIDTH, SCENE_HEIGHT);

            /* This will read an image from a file, create a DrawingLayer at the
             * same dimensions as the file, then copy the image to the DrawingLayer */
            m_bitmap = factory.CreateDrawingLayerFromFile(@".\Assets\Radioactive.jpg");

            /* Create a new ripple effect */
            m_rippleEffect = new RippleEffect(factory);

            nullRipple = new RippleSettings()
                {
                    Amplitude = 0f,
                    LightIntensity = 0f
                };
        }

        public override void Render()
        {
            /* This will clear the drawing layer.  For this sample, it is unneeded 
             * because we are overwriting the entire drawing layer with an opaque image */
            m_tempLayer.Clear();

            /* Let our drawing layer we want to start drawing */
            m_tempLayer.BeginDraw();

            /* Draw our loaded image on to our temp layer, and size it to fit it perfectly */
            m_tempLayer.DrawLayer(m_bitmap,
                                  new RectangleF(0, 0, m_tempLayer.Width, m_tempLayer.Height));

            /* Let our drawing layer know we are done drawing */
            m_tempLayer.EndDraw();

            var positions = new List<RippleSettings>();
            m_ripples.ToList().ForEach(kvp => positions.Add(kvp.Value));

            for (int i = 0; i < positions.Count; i++)
            {
                var sourceLayer = m_tempLayer;
                var targetLayer = m_mainLayer;
                if (i % 2 == 1)
                {
                    sourceLayer = m_mainLayer;
                    targetLayer = m_tempLayer;
                }
                positions[i].Update();
                positions[i].Apply(m_rippleEffect, sourceLayer, targetLayer);
            }

            if (m_ripples.Count % 2 == 0)
            {
                nullRipple.Apply(m_rippleEffect, m_tempLayer, m_mainLayer);
            }
        }

        private PointF ConvertPositionToRelative(PointF position)
        {
            return new PointF(position.X / m_mainLayer.Width,
                              position.Y / m_mainLayer.Height);
        }

        public override void SetInputState(int inputId, InputStatus status, PointF position)
        {
            if (status == InputStatus.Down)
            {
                if (!m_ripples.ContainsKey(inputId))
                {

                    m_ripples.Add(inputId, new RippleSettings() { Center = ConvertPositionToRelative(position)});
                }
                else
                {
                    m_ripples[inputId].Center = ConvertPositionToRelative(position);
                }
            }
            else if (m_ripples.ContainsKey(inputId))
            {
                m_ripples.Remove(inputId);
            }
            
        }
    }
}
